﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using Storage;
using Storage.EntityModels;
using System.Data.Linq;
using Stats.Objects;

namespace Stats
{
    /// <summary>
    /// This class contains the method used to update the daily statistics table.
    /// </summary>
    public class UpdateDailyStatsTableModule
    {
        /// <summary>
        /// This method updates (or inserts if not exists) the entry in the DailyStatistics table related to the specified date. 
        /// </summary>
        /// <param name="date">The date that specifies the entry in the table to insert or update</param>
        /// <param name="addMissedAppointmentByDriver">This boolean specifies if a missed appointment by driver should be added to the date entry during update</param>
        /// <param name="addMissedAppointmentByPassenger">This boolean specifies if a missed appointment by passenger should be added to the date entry during update</param>
        /// <returns>A boolean TRUE = update OK.....FALSE = update failed</returns>
        public static bool updateDailyStatsTable(DateTime date, bool addMissedAppointmentByDriver, bool addMissedAppointmentByPassenger)
        {
            bool recoveryOk = recoveryDailyStatsTable(date.AddDays(-1)); //check if the table is updated up to the day before

            if (!recoveryOk) return false;

            else
            {
                DailyStatistics dailyEntryToAddOrUpdate;

                TripSavings trSav;

                bool updateOK = true;

                int dailyTotalPassengersCarried = 0;
                int dailyMaxPassengersCarried = 0;
                int dailyPassengersCarriedOnTheWayCount = 0;
                int tripsJoinedCount = 0;

                double dailyTotalKm = 0;
                double dailyAverageKm = 0;
                double dailyEmissionsSaved = 0;
                double dailyFuelSaved = 0;
                double dailyMoneySaved = 0;

                bool currentTripJoined = false;
                bool entryAlreadyExists = false;
                int tripTempMax = 0;

                try
                {
                    TripLoaDataContext db = Storage.StorageManager.Instance.Entities;

                    var entryforDateExists =
                        from entry in db.DailyStatistics
                        where entry.Date.Year.Equals(date.Year)
                            && entry.Date.Month.Equals(date.Month)
                            && entry.Date.Day.Equals(date.Day)
                        select entry;

                    var dailyAllTripsEntries =
                        (from dailyTrips in db.Trips
                         where dailyTrips.DepartureDate.Value.Year.Equals(date.Year)
                            && dailyTrips.DepartureDate.Value.Month.Equals(date.Month)
                            && dailyTrips.DepartureDate.Value.Day.Equals(date.Day)
                            && dailyTrips.Draft == false
                         select dailyTrips);

                    foreach (var dailyTrip in dailyAllTripsEntries)
                    {
                        int tripPassengers = AuxTripMethods.getTripPassengersCount(dailyTrip.Id);
                        currentTripJoined =  tripPassengers != 0;

                        if (currentTripJoined)
                        {
                            tripTempMax = AuxTripMethods.getTripMaxPassengers(dailyTrip.Id);
                            if (tripTempMax > dailyMaxPassengersCarried) dailyMaxPassengersCarried = tripTempMax;

                            dailyTotalKm += AuxDistanceMethods.getTripKm(dailyTrip.Id);
                            dailyTotalPassengersCarried += tripPassengers;
                            dailyPassengersCarriedOnTheWayCount += AuxTripMethods.getPassengersCarriedOnTheWayCountForTrip(dailyTrip.Id);

                            trSav = AuxSavingsMethods.getTripSavings(dailyTrip.Id);

                            dailyEmissionsSaved += trSav.TripEmissionsSaved;
                            dailyFuelSaved += trSav.TripFuelSaved;
                            dailyMoneySaved += trSav.TripMoneySaved;

                            tripsJoinedCount = tripsJoinedCount + 1;
                        }

                        currentTripJoined = false;
                    }

                    if (tripsJoinedCount > 0 && dailyTotalKm > 0)  dailyAverageKm = dailyTotalKm / tripsJoinedCount;
                    else dailyAverageKm = 0;

                    #region IF ENTRY NOT EXISTS --- WILL BE CREATED

                    if (entryforDateExists.Count() == 0)
                    {
                        dailyEntryToAddOrUpdate = new DailyStatistics();

                        if (addMissedAppointmentByDriver == true) { dailyEntryToAddOrUpdate.MissedAppointmentsByDrivers = 1; }
                        else { dailyEntryToAddOrUpdate.MissedAppointmentsByDrivers = 0; }

                        if (addMissedAppointmentByPassenger == true) { dailyEntryToAddOrUpdate.MissedAppointmentsByPassengers = 1; }
                        else { dailyEntryToAddOrUpdate.MissedAppointmentsByPassengers = 0; }

                    }

                    #endregion

                    #region ELSE THE ENTRY EXISTS --- WILL BE UPDATED

                    else
                    {
                        dailyEntryToAddOrUpdate = entryforDateExists.First();
                        entryAlreadyExists = true;

                    }

                    #endregion

                    //System.Console.WriteLine("DATE TO ADD IN DAILY TABLE: " + date.ToShortDateString());
                    //System.Console.WriteLine("DAILY PROPOSED TRIPS: " + dailyTripEntries.Count());
                    //System.Console.WriteLine("DAILY COMPLETED TRIPS: " + dailyCompletedTripsCount);
                    //System.Console.WriteLine("DAILY TOTAL KM: " + dailyTotalKm);
                    //System.Console.WriteLine("DAILY AVERAGE KM: " + dailyAverageKm);
                    //System.Console.WriteLine("DAILY TOTAL PSN CARRIED: " + dailyTotalPassengersCarried);
                    //System.Console.WriteLine("DAILY MSX PSN CARRIED IN A TRIP: " + dailyMaxPassengersCarried);
                    //System.Console.WriteLine("DAILY PSN CARRIED ON THE WAY COUNT: " + dailyPassengersCarriedOnTheWayCount);

                    dailyEntryToAddOrUpdate.Date = date;
                    dailyEntryToAddOrUpdate.TotalDailyTripProposal = (short)dailyAllTripsEntries.Count();
                    dailyEntryToAddOrUpdate.TotalKilometers = (float)dailyTotalKm;
                    dailyEntryToAddOrUpdate.TotalPassengers = (short)dailyTotalPassengersCarried;
                    dailyEntryToAddOrUpdate.MaximumPassengers = dailyMaxPassengersCarried;
                    dailyEntryToAddOrUpdate.DailyCompletedTrips = (short)tripsJoinedCount;
                    dailyEntryToAddOrUpdate.PassengerCarriedAlongTheWay = (short)dailyPassengersCarriedOnTheWayCount;
                    dailyEntryToAddOrUpdate.AverageTripKm = (float)dailyAverageKm;
                    dailyEntryToAddOrUpdate.FuelSaved = (float)dailyFuelSaved;
                    dailyEntryToAddOrUpdate.MoneySaved = (decimal)dailyMoneySaved;
                    dailyEntryToAddOrUpdate.PollutionSaved = (float)dailyEmissionsSaved;

                    // Missed Appointments will be calculated when users will left feedback for the trips

                    dailyEntryToAddOrUpdate.RegisteredUsersCount = AuxMethods.getRegisteredUsersCountOperation();

                    if (!entryAlreadyExists) db.DailyStatistics.InsertOnSubmit(dailyEntryToAddOrUpdate);

                    db.SubmitChanges();

                }
                catch (InvalidOperationException) { updateOK = false; }
                catch (NullReferenceException) { updateOK = false; }
                catch (System.Data.SqlClient.SqlException) { updateOK = false; }


                return updateOK;
            }
        }

        /// <summary>
        /// This method recovers the DailyStatistics table if it's not up to the param date. The recovery includes the entry with date equals to param date
        /// </summary>
        /// <param name="date">The date that specifies the entry in the table whose existence has to be checked and, in case, recovered</param>
        /// <returns>A boolean TRUE = recover OK: in the table there are all the entries with date preceding param date .....FALSE = recover failed</returns>
        private static bool recoveryDailyStatsTable(DateTime date){

            bool recoveryOK = true;
            try
            { 
                TripLoaDataContext db = Storage.StorageManager.Instance.Entities;

                var numbEntries = db.DailyStatistics.Count();

                if (numbEntries != 0)//if table is not empty
                {
                    var entryforDateExists =
                        from entry in db.DailyStatistics
                        where entry.Date.Year.Equals(date.Year)
                            && entry.Date.Month.Equals(date.Month)
                            && entry.Date.Day.Equals(date.Day)
                        select entry;

                    if (entryforDateExists.Count() == 0) //entry for date "date" doesn't exist
                    {
                        var lastEntryDate =
                             from entry in db.DailyStatistics
                             orderby entry.Date descending
                             select entry.Date;

                        DateTime lastDate = lastEntryDate.First();

                        //Inserts all the missing entries whose dates are in the range lastDate-date
                        while (lastDate.CompareTo(date)<0 && recoveryOK)
                        {
                            lastDate = lastDate.AddDays(1);
                            recoveryOK= updateDailyStatsTable(lastDate, false, false);
                            //Console.WriteLine("Recovery entry for " + lastDate);
                        }
                    }
                    //Console.WriteLine("Recovered " + date + " " + recoveryOK);
                }
            }
            catch (InvalidOperationException) { recoveryOK = false; }
            catch (NullReferenceException) { recoveryOK = false; }
            catch (System.Data.SqlClient.SqlException) { recoveryOK = false; }

            return recoveryOK;

        }
    }
}
